/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package uk.ac.rdg.resc.mmd.controllers;

import java.awt.Color;
import java.awt.Font;
import java.awt.geom.Ellipse2D;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.SortedMap;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ColorBar;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.ContourPlot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.LookupPaintScale;
import org.jfree.chart.renderer.xy.XYBlockRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.contour.ContourDataset;
import org.jfree.data.contour.DefaultContourDataset;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.DefaultXYZDataset;
import org.joda.time.DateTime;

/**
 *
 * @author Alastair
 */
public class JFreeChartController {

    private static final Color[] defaultColors = new Color[] {
        new Color(0,0,143), new Color(0,0,159), new Color(0,0,175),
        new Color(0,0,191), new Color(0,0,207), new Color(0,0,223),
        new Color(0,0,239), new Color(0,0,255), new Color(0,11,255),
        new Color(0,27,255), new Color(0,43,255), new Color(0,59,255),
        new Color(0,75,255), new Color(0,91,255), new Color(0,107,255),
        new Color(0,123,255), new Color(0,139,255), new Color(0,155,255),
        new Color(0,171,255), new Color(0,187,255), new Color(0,203,255),
        new Color(0,219,255), new Color(0,235,255), new Color(0,251,255),
        new Color(7,255,247), new Color(23,255,231), new Color(39,255,215),
        new Color(55,255,199), new Color(71,255,183), new Color(87,255,167),
        new Color(103,255,151), new Color(119,255,135), new Color(135,255,119),
        new Color(151,255,103), new Color(167,255,87), new Color(183,255,71),
        new Color(199,255,55), new Color(215,255,39), new Color(231,255,23),
        new Color(247,255,7), new Color(255,247,0), new Color(255,231,0),
        new Color(255,215,0), new Color(255,199,0), new Color(255,183,0),
        new Color(255,167,0), new Color(255,151,0), new Color(255,135,0),
        new Color(255,119,0), new Color(255,103,0), new Color(255,87,0),
        new Color(255,71,0), new Color(255,55,0), new Color(255,39,0),
        new Color(255,23,0), new Color(255,7,0), new Color(246,0,0),
        new Color(228,0,0), new Color(211,0,0), new Color(193,0,0),
        new Color(175,0,0), new Color(158,0,0), new Color(140,0,0)
        };

    // Create a Timeseries Chart of the model and obs data using JFreeChart, and
    // Stream it to the provided OutputStream
    public static void streamTimechart(OutputStream out, SortedMap<DateTime, Float> obsData,
            SortedMap<DateTime, Float> modelData, HashMap<String, String> labels) throws IOException {

        TimeSeriesCollection obsDataset = createTimeseriesCollection(obsData, "Obs Data");
        TimeSeriesCollection modelDataset = createTimeseriesCollection(modelData, "Model Data");

        // Create a chart with no legend, tooltips or URLs. Just one (obs)
        // dataset, and just one (obs) y-axis at this point.
        String title = labels.get("Title");
        String yLabel = labels.get("ObsYAxisLabel");
        JFreeChart chart = ChartFactory.createTimeSeriesChart(title,
                "Date / time", yLabel, obsDataset, false, false, false);

        final XYPlot plot = chart.getXYPlot();
        plot.setRenderer(0, getRenderer(new Color(255,0,0))); //obs - red
        plot.setRenderer(1, getRenderer(new Color(0,0,255))); //model - blue

        plot.setNoDataMessage("There is no data for your choice");
        plot.setNoDataMessageFont(new Font("sansserif", Font.BOLD, 32));

        // Create a clear font for the y-axes labels to double as a legend
        Font yAxesLabelFont = new Font("sansserif", Font.BOLD, 20);

        // Create a secondary y-axis for the model data, with a blue label
        final NumberAxis axis2 = new NumberAxis(labels.get("ModelYAxisLabel"));
        axis2.setLabelPaint(new Color(0,0,255));
        axis2.setLabelFont(yAxesLabelFont);
        axis2.setAutoRangeIncludesZero(false);
        plot.setRangeAxis(1, axis2);

        // Set the label font and colour to red for the original (obs) y-axis
        plot.getRangeAxis(0).setLabelPaint(new Color(255,0,0));
        plot.getRangeAxis(0).setLabelFont(yAxesLabelFont);

        // Add the model dataset, and map it to the secondary model y-axis
        plot.setDataset(1, modelDataset);
        plot.mapDatasetToRangeAxis(1, 1);
        
        ChartUtilities.writeChartAsPNG(out, chart, 600, 450);
    }

    public static void streamFilledBlockChart(OutputStream out, double[][] data, int blockHeight) throws IOException {
        DefaultXYZDataset dataset = new DefaultXYZDataset();
        dataset.addSeries("Temperature (degC)", data);
        NumberAxis xAxis = new NumberAxis("Cumulative Month");
        xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        xAxis.setLowerMargin(0.0);
        xAxis.setUpperMargin(0.0);
        NumberAxis yAxis = new NumberAxis("Depth (m)");
        yAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        yAxis.setLowerMargin(0.0);
        yAxis.setUpperMargin(0.0);
        XYBlockRenderer renderer = new XYBlockRenderer();
        renderer.setBlockHeight(blockHeight);

        double min = 0.0;
        double max = 28.0;
        double scaleRange = max - min;
        LookupPaintScale scale = new LookupPaintScale(min, max, new Color(0,0,0));
        for (int iColor = 0; iColor < defaultColors.length; iColor++) {
            scale.add(min + (scaleRange * ((double)iColor / (defaultColors.length - 1))), defaultColors[iColor]);
        }

        renderer.setPaintScale(scale);
        XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer);
        plot.setBackgroundPaint(Color.lightGray);
        plot.setDomainGridlinesVisible(false);
        plot.setRangeGridlinePaint(Color.white);
        JFreeChart chart = new JFreeChart("Filled Block Depth-Time Plot", plot);
        chart.removeLegend();
        chart.setBackgroundPaint(Color.white);
        ChartUtilities.writeChartAsPNG(out, chart, 600, 450);
    }

    public static void streamContourPlot(OutputStream out, Double[][] data, double deepestDepth) throws IOException {
        Double[] times = data[0];
        Double[] depths = data[1];
        Double[] values = data[2];
        ContourDataset contourDataset = new DefaultContourDataset("Test", times, depths, values);
        NumberAxis yAxis = new NumberAxis("Depth (m)");
        NumberAxis xAxis = new NumberAxis("Cumulative Month");
        xAxis.setUpperMargin(0.0);
        yAxis.setUpperMargin(0.0);
        yAxis.setLowerBound(deepestDepth);
        xAxis.setAutoRangeIncludesZero(false);

        ColorBar zColorBar = new ColorBar("Temperature (degC)");
        ContourPlot cplot = new ContourPlot(contourDataset, xAxis, yAxis, zColorBar);

        final JFreeChart chart = new JFreeChart("Contour Depth-Time Plot", cplot);
        chart.removeLegend();
        ChartUtilities.writeChartAsPNG(out, chart, 600, 450);
    }

    // Create a renderer for the datasets. Only the colour can be tailored here
    private static XYLineAndShapeRenderer getRenderer(Color color) {
        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
        renderer.setSeriesShape(0, new Ellipse2D.Double(-2.0, -2.0, 4.0, 4.0));
        renderer.setSeriesShapesVisible(0, true);
        renderer.setSeriesPaint(0, color);
        return renderer;
    }

    // Take the actual data and use them to create a TimeSeriesCollection required
    // by JFreeChart to create a dual axis timeseries chart.
    private static TimeSeriesCollection createTimeseriesCollection(SortedMap<DateTime, Float> data, String name) {
        TimeSeries ts = new TimeSeries(name, Millisecond.class);
        for (DateTime dateTime : data.keySet()) {
            ts.add(new Millisecond(dateTime.toDate()), data.get(dateTime));
        }

        TimeSeriesCollection dataset = new TimeSeriesCollection();
        dataset.addSeries(ts);
        return dataset;
    }

}
